/*
 * Copyright (c) 1999-2004 eVelopers Corporation. All rights reserved.
 *
 * This is open source software; you can use, redistribute and/or modify 
 * it under the terms of the Open Software Licence v 2.1 as published by the Open 
 * Source Initiative.
 *
 * You should have received a copy of the Open Software Licence along with this
 * application; if not, contact the Open Source Initiative (http://opensource.org).
 */
package com.unimod.fsml.validation;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IMarker;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.resources.WorkspaceJob;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.jdt.core.ElementChangedEvent;
import org.eclipse.jdt.core.IElementChangedListener;
import org.eclipse.jdt.core.JavaCore;
import org.eclipse.swt.widgets.Display;
import org.eclipse.ui.IFileEditorInput;

import com.unimod.fsml.validation.problems.Problem;
import com.unimod.fsmleditor.FSMLEditor;

/**
 * <p>
 * Model Validator.
 * </p>
 * <p>
 * FSMLEditor and Validator has 1-1 relationship.
 * Validator is inited on editor start and disposed on editor dispose.
 * Validator listener for editor command stack and reacts 
 * on <strong>structure changes</strong> only. When model structure is changed, 
 * validator schedules Validation Job ({@link org.eclipse.core.resources.WorkspaceJob}).
 * Internal Eclipse job scheduler prevents two Validation Jobs from running concurrently.
 * Validation Job is being started in decoration mode. 
 * </p> 
 * <p>
 * For convenience purposes all real validation work moved to {@link InternalValidator} 
 * with which there is 1-1 relationship.
 * {@link InternalValidator} manages model resource markers and associated problems.  
 * </p>
 * <p>
 * Validator allows to get {@link com.unimod.fsml.validation.problems.Problem} associated
 * with given {@link org.eclipse.core.resources.IMarker} and to get all {@link com.unimod.fsml.validation.problems.Problem}
 * associated with given {@link com.evelopers.unimod.core.ModelElement} 
 * </p>
 */
public class Validator implements IElementChangedListener {
    private static final Log log = LogFactory.getLog(Validator.class);

	private Job validationJob;
	private InternalValidator validator;
	
	public Validator(FSMLEditor editor) {
		
		IFile inputFile = ((IFileEditorInput)editor.getEditorInput()).getFile(); 
		
		validator = new InternalValidator(
				Display.getCurrent(),
				inputFile);

		validationJob = new ValidationJob();
		validationJob.setRule(ResourcesPlugin.getWorkspace().getRuleFactory().markerRule(inputFile));
	
        JavaCore.addElementChangedListener(this,
                ElementChangedEvent.POST_CHANGE |
                ElementChangedEvent.POST_RECONCILE);
	}
	
	public void dispose() {
        JavaCore.removeElementChangedListener(this);

		validationJob.cancel();
		
		validator.unregisterAllProblems();
	}

	/**
	 * Starts validation job.
	 * If validation job already running and new validation requested,
	 * old validation will be canceled and new one will be 
	 * scheduled to run just after finishing current validation. 
	 */ 
	public void forceValidation() {
	    validationJob.cancel();
		validationJob.schedule(500);
	}
	
    /**
     * Listens to any change in JDT java model and forces validation.
     * @param event the change event 
     */
    public void elementChanged(ElementChangedEvent event) {
        forceValidation();
    }

	/**
	 * Returns problem associated with given Eclipse IMarker.
	 * 
	 * @param marker
	 * @return
	 */
	public Problem getProblem(IMarker marker) {
		return validator.getProblem(marker);
	}
	
	public Problem[] getProblems(ProblemToken problemToken) {
		return validator.getProblems(problemToken);
	}
	
	private class ValidationJob extends WorkspaceJob {
		
		private ValidationJob() {
			super("Validation job");
			setSystem(true);
			setPriority(Job.DECORATE);
		}
		
		public IStatus runInWorkspace(IProgressMonitor monitor) {
			log.debug("FSML model changed - start validation!");
			
			validator.validate(monitor);
			
			return Status.OK_STATUS;
		}
	}
}
